package com.firewolf.springboot.conf;

import java.lang.reflect.Method;

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * Redis缓存配置类，
 * RedisCacheConfig这里也可以不用继承CachingConfigurerSupport，也就是直接一个普通的Class就好了
 * 但是使用普通类的方式的话，如果需要自己重写主键生成策略， 就需要在使用@Cacheable的时候还需要指定KeyGenerator的名称;
 * 这样编码的时候比较麻烦 这里集成了CachingConfigurerSupport，重新实现 key的生成策略的时候只要这里修改KeyGenerator，
 * 其它位置不用修改就生效了
 * 
 * @author liuxing
 *
 */
@Configuration
@EnableCaching // 启用缓存，一定要加，否则缓存不生效
public class RedisCacheConfig extends CachingConfigurerSupport {

	/**
	 * 缓存管理器
	 * 
	 * @param redisTemplate
	 * @return
	 */
	@Bean
	public CacheManager cacheManager(RedisTemplate<?, ?> redisTemplate) {
		CacheManager cacheManager = new RedisCacheManager(redisTemplate);
		return cacheManager;
	}

	/**
	 * redis模板操作类,类似于jdbcTemplate的一个类;
	 * 
	 * 虽然CacheManager也能获取到Cache对象，但是操作起来没有那么灵活；
	 * 
	 * 这里在扩展下：RedisTemplate这个类不见得很好操作，我们可以在进行扩展一个我们
	 * 
	 * 自己的缓存类，比如：RedisStorage类;
	 * 
	 * @param factory
	 *            : 通过Spring进行注入，参数在application.properties进行配置；
	 * @return
	 */
	@Bean
	public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
		RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
		redisTemplate.setConnectionFactory(factory);

		// key序列化方式;（不然会出现乱码;）,但是如果方法上有Long等非String类型的话，会报类型转换错误；
		// 所以在没有自己定义key生成策略的时候，以下这个代码建议不要这么写，可以不配置或者自己实现ObjectRedisSerializer
		// 或者JdkSerializationRedisSerializer序列化方式;
		 RedisSerializer<String> redisSerializer = new
		 StringRedisSerializer();// Long类型不可以会出现异常信息;
		 redisTemplate.setKeySerializer(redisSerializer);
		 redisTemplate.setHashKeySerializer(redisSerializer);
		return redisTemplate;
	}

	/**
	 * 自定义key
       此方法将会根据调用的类名+方法名+所有参数的值生成唯一的一个key,即使@Cacheable中的value属性一样，key也会不一样。 
	 */
	@Override
	public KeyGenerator keyGenerator() {
		return new KeyGenerator() {
			@Override
			public Object generate(Object o, Method method, Object... objects) {
				StringBuilder sb = new StringBuilder();
				sb.append(o.getClass().getName());
//				sb.append(method.getName());//有这句话后会导致清楚缓存失败，因为这句话会导致存入的key和清楚清除时候的key不同
				for (Object obj : objects) {
					sb.append(obj.toString());
				}
				System.out.println("key = " + sb.toString());
				return sb.toString();
			}
		};
	}
}
